package com.ones.sdp.kit.extra.util;

import com.ones.sdp.common.core.exception.OnesServiceException;
import com.ones.sdp.common.core.util.SpringContextKit;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClient;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * 基于 Spring RestClient 的 HTTP 工具类（静态工具类）
 * 支持：
 * - 泛型返回值（Class<T> 和 ParameterizedTypeReference<T>）
 * - 自定义 HttpHeaders
 * - Basic Auth
 * 统一异常处理，抛出 OnesServiceException。
 *
 * @author Clark
 * @version 2025-08-23
 */
@Slf4j
@UtilityClass
public class RestKit {

    private static final RestClient restClient;

    static {
        restClient = SpringContextKit.getBean(RestClient.class);
        log.warn("******************* Rest工具类装配完成 *******************");
    }

    // ================== GET 方法（泛型支持）==================

    /**
     * GET 请求，返回指定类型的对象
     *
     * @param url          请求 URL
     * @param responseType 响应体的目标类型（Class<T>）
     * @param headers      可选请求头
     * @param <T>          目标类型
     * @return 反序列化后的对象
     * @throws OnesServiceException 请求失败时抛出
     */
    public <T> T getForObject(String url, Class<T> responseType, HttpHeaders headers) {
        return executeRequest(() -> {
            var requestSpec = restClient.get().uri(url);
            if (headers != null && !headers.isEmpty()) {
                requestSpec.headers(h -> h.addAll(headers));
            }
            return requestSpec.retrieve().body(responseType);
        });
    }

    /**
     * GET 请求，返回指定类型的对象
     *
     * @param url
     * @param responseType
     * @return
     * @param <T>
     */
    public <T> T getForObject(String url, Class<T> responseType) {
        return getForObject(url, responseType, null);
    }

    /**
     * GET 请求，支持泛型集合（如 List<User>、Map<String, Object>）
     *
     * @param url                        请求 URL
     * @param responseType               参数化类型引用（new ParameterizedTypeReference<>(){}）
     * @param headers                    可选请求头
     * @param <T>                        目标泛型类型
     * @return 反序列化后的泛型对象
     * @throws OnesServiceException 请求失败时抛出
     */
    public <T> T getForObject(String url, ParameterizedTypeReference<T> responseType, HttpHeaders headers) {
        return executeRequest(() -> {
            var requestSpec = restClient.get().uri(url);
            if (headers != null && !headers.isEmpty()) {
                requestSpec.headers(h -> h.addAll(headers));
            }
            return requestSpec.retrieve().body(responseType);
        });
    }

    /**
     * GET 请求（ParameterizedTypeReference）
     */
    public <T> T getForObject(String url, ParameterizedTypeReference<T> responseType) {
        return getForObject(url, responseType, null);
    }

    // ================== POST 方法（泛型支持）==================

    /**
     * POST JSON 请求，返回指定类型对象
     *
     * @param url          请求 URL
     * @param body         请求体（JSON 字符串或对象）
     * @param responseType 响应体的目标类型
     * @param headers      可选请求头
     * @param <T>          目标类型
     * @return 反序列化后的对象
     */
    public <T> T postForObject(String url, Object body, Class<T> responseType, HttpHeaders headers) {
        return executeRequest(() -> {
            var requestSpec = restClient.post()
                    .uri(url)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(body);

            if (headers != null && !headers.isEmpty()) {
                requestSpec = requestSpec.headers(h -> h.addAll(headers));
            }

            return requestSpec.retrieve().body(responseType);
        });
    }

    /**
     * POST JSON 请求，返回指定类型对象
     *
     * @param url          请求 URL
     * @param body         请求体（JSON 字符串或对象）
     * @param responseType 响应体的目标类型
     * @param <T>          目标类型
     * @return 反序列化后的对象
     */
    public <T> T postForObject(String url, Object body, Class<T> responseType) {
        return postForObject(url, body, responseType, null);
    }

    /**
     * POST JSON 请求，返回泛型类型（如 List<T>, Page<T>）
     *
     * @param url          请求 URL
     * @param body         请求体（JSON 字符串或对象）
     * @param responseType 响应体的目标类型
     * @param headers      可选请求头
     * @param <T>          目标类型
     * @return 反序列化后的对象
     */
    public <T> T postForObject(String url, Object body, ParameterizedTypeReference<T> responseType, HttpHeaders headers) {
        return executeRequest(() -> {
            var requestSpec = restClient.post()
                    .uri(url)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(body);

            if (headers != null && !headers.isEmpty()) {
                requestSpec = requestSpec.headers(h -> h.addAll(headers));
            }

            return requestSpec.retrieve().body(responseType);
        });
    }

    /**
     * POST JSON（ParameterizedTypeReference版本）
     *
     * @param url          请求 URL
     * @param body         请求体（JSON 字符串或对象）
     * @param responseType 响应体的目标类型
     * @param <T>          目标类型
     * @return 反序列化后的对象
     */
    public <T> T postForObject(String url, Object body, ParameterizedTypeReference<T> responseType) {
        return postForObject(url, body, responseType, null);
    }

    // ================== Basic Auth 支持泛型 ==================

    /**
     * 带 Basic 认证的 POST 请求，返回指定类型
     *
     * @param username
     * @param password
     * @param url
     * @param body
     * @param responseType
     * @param headers
     * @return
     * @param <T>
     */
    public <T> T postWithAuth(String username, String password, String url, Object body,
                              Class<T> responseType, HttpHeaders headers) {
        String credentials = username + ":" + password;
        String base64Credentials = Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));

        return executeRequest(() -> {
            var requestSpec = restClient.post()
                    .uri(url)
                    .contentType(MediaType.APPLICATION_JSON)
                    .header(HttpHeaders.AUTHORIZATION, "Basic " + base64Credentials)
                    .body(body);

            if (headers != null && !headers.isEmpty()) {
                requestSpec = requestSpec.headers(h -> h.addAll(headers));
            }

            return requestSpec.retrieve().body(responseType);
        });
    }

    /**
     * Basic Auth
     *
     * @param username
     * @param password
     * @param url
     * @param body
     * @param responseType
     * @return
     * @param <T>
     */
    public <T> T postWithAuth(String username, String password, String url, Object body, Class<T> responseType) {
        return postWithAuth(username, password, url, body, responseType, null);
    }

    // ================== 字符串返回 ==================

    /**
     * GET 返回字符串
     *
     * @param url
     * @param headers
     * @return
     */
    public String get(String url, HttpHeaders headers) {
        return getForObject(url, String.class, headers);
    }

    /**
     * GET 返回字符串
     *
     * @param url
     * @return
     */
    public String get(String url) {
        return get(url, null);
    }

    /**
     * POST 返回字符串
     *
     * @param url
     * @param body
     * @param headers
     * @return
     */
    public String post(String url, String body, HttpHeaders headers) {
        return postForObject(url, body, String.class, headers);
    }

    /**
     * POST 返回字符串
     *
     * @param url
     * @param body
     * @return
     */
    public String post(String url, String body) {
        return post(url, body, null);
    }

    /**
     * 执行 HTTP 请求
     * @param requestSupplier
     * @return
     * @param <T>
     */
    private <T> T executeRequest(RequestSupplier<T> requestSupplier) {
        try {
            log.debug("执行 HTTP 请求...");
            T result = requestSupplier.execute();
            log.debug("HTTP 请求成功");
            return result;
        } catch (ResourceAccessException ex) {
            log.error("外部服务连接异常（超时/网络问题）: {}", ex.getMessage(), ex);
            throw new OnesServiceException("外部服务连接超时或不可达");
        } catch (HttpClientErrorException | HttpServerErrorException ex) {
            String statusText = ex.getStatusText();
            String responseBody = ex.getResponseBodyAsString();
            log.error("外部服务返回错误状态 [{} {}]: {}", ex.getStatusCode(), statusText, responseBody);
            throw new OnesServiceException(
                    String.format("外部服务异常: %s (%s) - %s",
                            ex.getStatusCode(), statusText,
                            responseBody.isEmpty() ? "无响应体" : responseBody)
            );
        } catch (OnesServiceException ex) {
            throw ex;
        } catch (Exception ex) {
            log.error("调用外部服务时发生未知错误: {}", ex.getMessage(), ex);
            throw new OnesServiceException("调用外部服务时发生未知错误");
        }
    }

    /**
     * 函数式接口，定义 HTTP 请求
     * @param <T>
     */
    @FunctionalInterface
    private interface RequestSupplier<T> {
        T execute() throws Exception;
    }
}